#ifndef FILE_HPP_
#define FILE_HPP_

class File {
	private:
		unsigned int file_size;
		int file_descriptor;
		FILE* file;
		char* path_;
		
		char* mapped_file;
		unsigned int mapped_size;
		unsigned int offset;
		bool getsize() {
			struct stat buf;
			if (fstat(file_descriptor, &buf) != -1) {
				file_size = (unsigned int)buf.st_size;
				return true;
			} else {
				return false;
			}
		}
		bool open_descriptor(bool file_in) {
			file_descriptor = open(path_, O_RDWR | ((!file_in)?(O_CREAT | O_TRUNC):0), 0666);
			return (file_descriptor > 0);
		}
	public:
		File(const char* path, bool file_in, int fd) {
			int path_len = strlen(path);
			path_ = (char*)malloc(path_len * sizeof(char));
			path_ = strncpy(path_, path, path_len);
			if (fd < 0) {
				if (!open_descriptor(file_in)) {
					perror("Failed to open descriptor");
				}
			} else {
				file_descriptor = fd;
			}
			if (!getsize()) {
				perror("Failed to get size of file");
				file_size = 0;
			}
			file = fopen(path, "r+");
			if (file == NULL) {
				perror("Failed to open stream to file");
			}
			mapped_file = NULL;
			mapped_size = 0;
			offset = 0;
		}
		//writing integer via FILE stream
		static int write_stream_integer(File& target_file, int value) {
			return fprintf(target_file.get_stream(), "%d,", value);
		}
		//read integer via FILE stream
		static int read_stream_integer(File& target_file, int* status) {
			int value;
			*status = fscanf(target_file.get_stream(), "%d,", &value);
			return value;
		}
		FILE *get_stream() {
			return file;
		}
		//method maps size bytes, making offset, returns NULL if mmap or munmap failed
		//maybe this method is illegal for standart user(see fileiterator)
		//in general class file requires that stuff
		char* map(unsigned int size, unsigned int offset) {
			if (mapped_file != NULL) {
				int status = munmap(mapped_file, mapped_size);
				if (status == -1) {
					perror("munmap failed");
					return NULL;
				}
			}
			mapped_size = size;
			(*this).offset = offset;
			mapped_file = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, offset);
			if (mapped_file == MAP_FAILED) {
				perror("file mmap failed");
				return NULL;
			}
			return mapped_file;
		}
		inline int get_descriptor() {
			return file_descriptor;
		}
		inline char* get_path() {
			return path_;
		}
		inline unsigned int get_size() {
			return file_size;
		}
		void set_descriptor(int fd) {
			file_descriptor = fd;
		}
		~File() {
			if (path_ != NULL) 
				free(path_);
			if (mapped_file != NULL) munmap(mapped_file, mapped_size);
			if (file_descriptor != -1) close(file_descriptor);
			if (file != NULL) fclose(file);
		}
};
#endif